package org.nd4j.reflectionloader; import org.nd4j.shade.jackson.databind.ObjectMapper; import org.nd4j.shade.jackson.databind.module.SimpleAbstractTypeResolver; import org.nd4j.shade.jackson.databind.module.SimpleModule; import org.nd4j.shade.jackson.dataformat.yaml.YAMLFactory; import org.reflections.Reflections; import java.io.IOException; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Created by agibsonccc on 3/10/17. */ public class JacksonReflectionLoader { /** * * @param types * @return */ public static ObjectMapper findTypesFor(List<Class<?>> types) { return findTypesFor(types, true); } /** * Get the implementations for a given list of classes. * These classes MUST be abstract classes or interfaces * @param types the types to get hte sub classes for * @return a map containing a list of interface names to * implementation types */ public static Map<String, String> getImpls(List<Class<?>> types) { Map<String, String> classes = new HashMap<>(); for (Class<?> type : types) { Reflections reflections = new Reflections(); Set<Class<?>> subClasses = (Set<Class<?>>) reflections.getSubTypesOf(type); if (subClasses.size() > 1) { throw new IllegalArgumentException(String.format( "Class " + type + " type can't be inferred. There is more than %d of sub class for the given class", subClasses.size())); } else if (subClasses.isEmpty()) throw new IllegalArgumentException("No class implementation found for " + type.getCanonicalName()); classes.put(type.getCanonicalName(), subClasses.iterator().next().getCanonicalName()); } return classes; } /** * * @param types * @param json * @return */ public static ObjectMapper findTypesFor(List<Class<?>> types, boolean json) { return withTypes(json ? new ObjectMapper() : new ObjectMapper(new YAMLFactory()), getImpls(types)); } /** * * @param objectMapper * @param typeImpls * @return */ public static ObjectMapper withTypes(ObjectMapper objectMapper, Map<String, String> typeImpls) { SimpleAbstractTypeResolver abstractTypeResolver = new SimpleAbstractTypeResolver(); SimpleModule simpleModule = new SimpleModule(); simpleModule.setAbstractTypes(abstractTypeResolver); for (Map.Entry<String, String> types : typeImpls.entrySet()) { try { Class interfaceClazz = Class.forName(types.getKey()); if (!interfaceClazz.isInterface()) throw new IllegalArgumentException( "Class key must be an interface. Found " + interfaceClazz.getSimpleName()); Class implClazz = Class.forName(types.getValue()); if (Modifier.isAbstract(implClazz.getModifiers()) || implClazz.isInterface()) throw new IllegalArgumentException("Class value must be a concrete implementation. Found " + implClazz.getSimpleName()); abstractTypeResolver.addMapping(interfaceClazz, implClazz); } catch (Exception e) { e.printStackTrace(); } } objectMapper.registerModule(simpleModule); return objectMapper; } /** * Instantiate the given class type * @param clazz the class to instantiate * @param json the json to instantiate from * @param objectMapper the object mapper to * @param <T> * @return * @throws IOException */ public static <T> T instantiateType(Class<T> clazz, String json, ObjectMapper objectMapper) throws IOException { return objectMapper.readValue(json, clazz); } }